package com.atty.dms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.atty.dms.common.convention.exception.ClientException;
import com.atty.dms.dao.entity.DormitoryDO;
import com.atty.dms.dao.entity.DormitoryStudentDO;
import com.atty.dms.dao.entity.UserDO;
import com.atty.dms.dao.mapper.*;
import com.atty.dms.dto.req.DormitoryStudentCFReqDTO;
import com.atty.dms.dto.req.DormitoryStudentCreateReqDTO;
import com.atty.dms.dto.req.DormitoryStudentUpdateReqDTO;
import com.atty.dms.dto.req.UserUpdateReqDTO;
import com.atty.dms.dto.resp.*;
import com.atty.dms.service.DormitoryService;
import com.atty.dms.service.DormitoryStudentService;
import com.atty.dms.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName:DormitoryStudentServiceImpl
 * Package:com.atty.dms.service.impl
 * Description:
 *
 * @Author:tony
 * @Create :2025/4/22 9:28
 * @Version :1.0
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DormitoryStudentServiceImpl extends ServiceImpl<DormitoryStudentMapper, DormitoryStudentDO> implements DormitoryStudentService {

    private final UserService userService;
    private final UserMapper userMapper;
    private final PreferenceMapper preferenceMapper;
    private final DormitoryService dormitoryService;
    private final DormitoryMapper dormitoryMapper;
    private final DormitoryStudentMapper dormitoryStudentMapper;

    // 定义偏好项权重
    private static final Map<String, Double> PREFERENCE_WEIGHTS = new HashMap<>() {{
        put("is_smoker", 2.0);
        put("accept_pets", 2.0);
        put("schedule_type", 1.8);
        put("cleanliness", 1.6);
        put("social_level", 1.4);
        put("study_in_dorm", 1.2);
        put("floor_preference", 1.0);
    }};

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignDormitoryStudent(DormitoryStudentCreateReqDTO requestParam) {
        log.info("开始分配宿舍，请求参数：{}", requestParam);

        // 校验宿舍id和学生id
        if (requestParam.getDormitoryId() == null || requestParam.getStudentId() == null) {
            log.error("宿舍id或学生id为空");
            throw new ClientException("宿舍id或学生id不能为空");
        }

        Long userId = requestParam.getStudentId();
        UserRespDTO userRespDTO = userService.getUserByUserId(userId);
        log.info("查询到的学生信息：{}", userRespDTO);
        if (userRespDTO == null) {
            log.error("学生不存在，学生ID：{}", userId);
            throw new ClientException("学生不存在");
        }
        if (userRespDTO.getHasDormitory() == 1) {
            log.error("学生已分配宿舍，学生ID：{}", userId);
            throw new ClientException("学生已分配宿舍");
        }
        if (userRespDTO.getRole() == null || !userRespDTO.getRole().equals(3)) {
            log.error("学生角色不存在，学生ID：{}", userId);
            throw new ClientException("学生角色不存在");
        }

        // 校验宿舍id
        Long dormitoryId = requestParam.getDormitoryId();
        DormitoryDO dormitoryDO = dormitoryService.getById(dormitoryId);
        if (dormitoryDO == null) {
            log.error("宿舍不存在，宿舍ID：{}", dormitoryId);
            throw new ClientException("宿舍不存在");
        }

        if (dormitoryDO.getCapacity() - dormitoryDO.getCurrentOccupancy() <= 0) {
            log.error("宿舍已满，宿舍ID：{}", dormitoryId);
            throw new ClientException("宿舍已满");
        }

        if (dormitoryDO.getIsAvailable() == 0) {
            log.error("宿舍不可用，宿舍ID：{}", dormitoryId);
            throw new ClientException("宿舍不可用");
        }

        if (dormitoryDO.getType() != userRespDTO.getGender()) {
            log.error("学生性别与宿舍类型不匹配，学生ID：{}，宿舍ID：{}", userId, dormitoryId);
            throw new ClientException("学生性别与宿舍类型不匹配");
        }

        // 更新宿舍的当前占用人数
        dormitoryDO.setCurrentOccupancy(dormitoryDO.getCurrentOccupancy() + 1);
        boolean dormitoryUpdated = dormitoryService.updateById(dormitoryDO);
        if (!dormitoryUpdated) {
            log.error("更新宿舍信息失败，宿舍ID：{}", dormitoryId);
            throw new ClientException("更新宿舍信息失败");
        }

        try {
            // 修改学生的 hasDormitory 字段
            userRespDTO.setHasDormitory(1);
            UserUpdateReqDTO userUpdateReqDTO = BeanUtil.toBean(userRespDTO, UserUpdateReqDTO.class);
            if (userUpdateReqDTO == null || userUpdateReqDTO.getUserId() == null) {
                log.error("用户数据转换失败，学生ID：{}", userId);
                throw new ClientException("用户数据转换失败");
            }
            userService.update(userUpdateReqDTO);

            // 插入数据到 dormitory_student 表中
            insertDormitoryStudentRecord(requestParam.getDormitoryId(), requestParam.getStudentId());
        } catch (Exception e) {
            log.error("修改学生宿舍状态失败，学生ID：{}，错误信息：{}", userId, e.getMessage(), e);
            throw new ClientException("分配宿舍失败：" + e.getMessage());
        }

        log.info("宿舍分配成功，学生ID：{}，宿舍ID：{}", userId, dormitoryId);
    }

    /**
     * 插入数据到 dormitory_student 表中
     *
     * @param dormitoryId 宿舍ID
     * @param studentId   学生ID
     */
    private void insertDormitoryStudentRecord(Long dormitoryId, Long studentId) {
        log.info("开始插入宿舍学生记录，宿舍ID：{}，学生ID：{}", dormitoryId, studentId);

        DormitoryStudentDO dormitoryStudentDO = DormitoryStudentDO.builder()
                .dormitoryId(dormitoryId)
                .studentId(studentId)
                .build();

        boolean save = save(dormitoryStudentDO);
        if (!save) {
            log.error("插入宿舍学生信息失败，宿舍ID：{}，学生ID：{}", dormitoryId, studentId);
            throw new ClientException("插入宿舍学生信息失败");
        }

        log.info("插入宿舍学生记录成功，宿舍ID：{}，学生ID：{}", dormitoryId, studentId);
    }

    @Override
    public List<DormitoryStudentRespDTO> getDormitoryStudentByDormitoryId(Long dormitoryId) {
        // 1. 获取该宿舍下的所有学生 ID
        List<Long> studentIds = dormitoryStudentMapper.getStudentIdsByDormitoryId(dormitoryId);
        if (studentIds == null || studentIds.isEmpty()) {
            return Collections.emptyList(); // 如果没有学生，返回空列表
        }

        // 2. 获取宿舍信息
        DormitoryRespDTO dormitoryRespDTO = dormitoryService.getDormitoryById(String.valueOf(dormitoryId));
        if (dormitoryRespDTO == null) {
            throw new ClientException("无法通过id找到宿舍信息: " + dormitoryId);
        }
        String dormitoryNumber = dormitoryRespDTO.getDormitoryNumber();
        Integer type = dormitoryRespDTO.getType();
        if (dormitoryNumber == null || type == null) {
            throw new ClientException("无法通过id找到宿舍信息: " + dormitoryId);
        }

        // 3. 获取每个学生的姓名，并构建返回结果
        List<DormitoryStudentRespDTO> result = studentIds.stream()
                .map(studentId -> {
                    String studentName = userService.getUserByUserId(studentId).getUsername();
                    return DormitoryStudentRespDTO.builder()
                            .studentId(studentId)
                            .studentName(studentName)
                            .type(type)
                            .dormitoryNumber(dormitoryNumber)
                            .dormitoryId(dormitoryId)
                            .build();
                })
                .collect(Collectors.toList());
        return result;
    }

    @Override
    public DormitoryStudentRespDTO getDormitoryStudentByStudentId(Long studentId) {
        LambdaQueryWrapper<DormitoryStudentDO> queryWrapper = Wrappers.lambdaQuery(DormitoryStudentDO.class)
                .eq(DormitoryStudentDO::getStudentId, studentId);
        DormitoryStudentDO dormitoryStudentDO = baseMapper.selectOne(queryWrapper);
        if (dormitoryStudentDO == null) {
            throw new ClientException("未找到该学生");
        }
        Long dormitoryId = dormitoryStudentDO.getDormitoryId();
        String username = userService.getUserByUserId(studentId).getUsername();

        DormitoryRespDTO dormitoryRespDTO = dormitoryService.getDormitoryById(String.valueOf(dormitoryId));
        String dormitoryNumber = dormitoryRespDTO.getDormitoryNumber();
        Integer type = dormitoryRespDTO.getType();
        return DormitoryStudentRespDTO.builder()
                .studentId(studentId)
                .studentName(username)
                .dormitoryNumber(dormitoryNumber)
                .dormitoryId(dormitoryId)
                .type(type)
                .build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDormitoryStudent(DormitoryStudentUpdateReqDTO requestParam) {
        log.info("开始更新宿舍分配，请求参数：{}", requestParam);
        // 校验宿舍 ID 和学生 ID
        if (requestParam.getDormitoryId() == null || requestParam.getStudentId() == null) {
            log.error("宿舍 ID 或学生 ID 为空");
            throw new ClientException("宿舍 ID 或学生 ID 不能为空");
        }
        Long studentId = requestParam.getStudentId();
        Long newDormitoryId = requestParam.getDormitoryId();
        // 获取学生信息
        UserRespDTO userRespDTO = userService.getUserByUserId(studentId);
        if (userRespDTO == null) {
            log.error("学生不存在，学生 ID：{}", studentId);
            throw new ClientException("学生不存在");
        }
        // 检查学生是否已经分配了宿舍
        if (userRespDTO.getHasDormitory() != 1) {
            log.error("学生尚未分配宿舍，无法更新，学生 ID：{}", studentId);
            throw new ClientException("学生尚未分配宿舍");
        }
        // 获取当前宿舍 ID
        DormitoryStudentRespDTO dormitoryStudentRespDTO = getDormitoryStudentByStudentId(studentId);
        if (dormitoryStudentRespDTO == null) {
            log.error("未找到学生的宿舍分配记录，学生 ID：{}", studentId);
            throw new ClientException("未找到学生的宿舍分配记录");
        }
        Long oldDormitoryId = dormitoryStudentRespDTO.getDormitoryId();

        // 如果新宿舍和旧宿舍相同，无需更新
        if (Objects.equals(oldDormitoryId, newDormitoryId)) {
            log.warn("学生已经在目标宿舍，无需更新，学生 ID：{}", studentId);
            return;
        }

        // 校验新宿舍的合法性
        DormitoryDO newDormitory = dormitoryService.getById(newDormitoryId);
        if (newDormitory == null) {
            log.error("新宿舍不存在，宿舍 ID：{}", newDormitoryId);
            throw new ClientException("新宿舍不存在");
        }

        if (newDormitory.getCapacity() - newDormitory.getCurrentOccupancy() <= 0) {
            log.error("新宿舍已满，宿舍 ID：{}", newDormitoryId);
            throw new ClientException("新宿舍已满");
        }

        if (newDormitory.getIsAvailable() == 0) {
            log.error("新宿舍不可用，宿舍 ID：{}", newDormitoryId);
            throw new ClientException("新宿舍不可用");
        }

        if (newDormitory.getType() != userRespDTO.getGender()) {
            log.error("学生性别与新宿舍类型不匹配，学生 ID：{}，宿舍 ID：{}", studentId, newDormitoryId);
            throw new ClientException("学生性别与新宿舍类型不匹配");
        }

        // 校验旧宿舍的合法性
        DormitoryDO oldDormitory = dormitoryService.getById(oldDormitoryId);
        if (oldDormitory == null) {
            log.error("旧宿舍不存在，宿舍 ID：{}", oldDormitoryId);
            throw new ClientException("旧宿舍不存在");
        }

        // 更新宿舍占用人数
        oldDormitory.setCurrentOccupancy(oldDormitory.getCurrentOccupancy() - 1);
        newDormitory.setCurrentOccupancy(newDormitory.getCurrentOccupancy() + 1);

        boolean oldDormitoryUpdated = dormitoryService.updateById(oldDormitory);
        boolean newDormitoryUpdated = dormitoryService.updateById(newDormitory);

        if (!oldDormitoryUpdated || !newDormitoryUpdated) {
            log.error("更新宿舍占用人数失败，旧宿舍 ID：{}，新宿舍 ID：{}", oldDormitoryId, newDormitoryId);
            throw new ClientException("更新宿舍占用人数失败");
        }

        // 更新 dormitory_student 表中的宿舍 ID
        int updated = dormitoryStudentMapper.updateDormitoryForStudent(studentId, newDormitoryId);
        if (updated < 1) {
            log.error("更新宿舍分配记录失败，学生 ID：{}，新宿舍 ID：{}", studentId, newDormitoryId);
            throw new ClientException("更新宿舍分配记录失败");
        }
        log.info("宿舍分配更新成功，学生 ID：{}，从宿舍 ID：{} 移动到宿舍 ID：{}", studentId, oldDormitoryId, newDormitoryId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDormitoryStudent(Long studentId) {
        // 1. 更新宿舍占用人数
        DormitoryStudentRespDTO dormitoryStudentRespDTO = getDormitoryStudentByStudentId(studentId);
        if (dormitoryStudentRespDTO == null || dormitoryStudentRespDTO.getDormitoryId() == null) {
            throw new ClientException("未找到学生的宿舍分配记录或宿舍 ID 为空");
        }
        // 2. 删除宿舍分配记录
        int deleteResult = dormitoryStudentMapper.deleteDormitoryStudent(studentId);
        if (deleteResult < 1) {
            throw new ClientException("删除宿舍分配记录失败");
        }
        log.info("删除宿舍分配记录成功，学生 ID：{}", studentId);

        // 3. 更新用户的 hasDormitory 状态
        boolean userUpdateSuccess = updateUserHasDormitoryStatus(studentId);
        if (!userUpdateSuccess) {
            throw new ClientException("更新用户宿舍状态失败");
        }
        log.info("已将用户 hasDormitory 状态更新为 0，学生 ID：{}", studentId);


        Long dormitoryId = dormitoryStudentRespDTO.getDormitoryId();
        boolean dormitoryUpdateSuccess = updateDormitoryOccupancy(dormitoryId);
        if (!dormitoryUpdateSuccess) {
            throw new ClientException("更新宿舍占用人数失败");
        }
        log.info("更新宿舍占用人数成功，宿舍 ID：{}", dormitoryId);
    }

    /**
     * 更新用户的 hasDormitory 状态为 0
     */
    private boolean updateUserHasDormitoryStatus(Long studentId) {
        LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class)
                .eq(UserDO::getUserId, studentId)
                .set(UserDO::getHasDormitory, 0);
        return userService.update(updateWrapper);
    }

    /**
     * 更新宿舍占用人数
     */
    private boolean updateDormitoryOccupancy(Long dormitoryId) {
        DormitoryDO dormitoryDO = dormitoryService.getById(dormitoryId);
        if (dormitoryDO == null) {
            throw new ClientException("未找到对应的宿舍信息");
        }

        // 减少宿舍占用人数
        dormitoryDO.setCurrentOccupancy(dormitoryDO.getCurrentOccupancy() - 1);
        return dormitoryService.updateById(dormitoryDO);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exchangeDormitoryStudent(Long studentIdA, Long studentIdB) {
        log.info("开始交换学生宿舍，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);

        // 1. 校验学生 A 和学生 B 是否存在
        UserRespDTO userRespDTO_A = userService.getUserByUserId(studentIdA);
        UserRespDTO userRespDTO_B = userService.getUserByUserId(studentIdB);
        if (userRespDTO_A == null || userRespDTO_B == null) {
            log.error("学生不存在，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
            throw new ClientException("学生不存在");
        }

        // 2. 校验学生 A 和学生 B 是否已分配宿舍
        if (userRespDTO_A.getHasDormitory() != 1 || userRespDTO_B.getHasDormitory() != 1) {
            log.error("学生未分配宿舍，无法交换，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
            throw new ClientException("学生未分配宿舍，无法交换");
        }

        // 3. 获取学生 A 和学生 B 的宿舍信息
        DormitoryStudentDO dormitoryStudentDO_A = getDormitoryStudentDOByStudentId(studentIdA);
        DormitoryStudentDO dormitoryStudentDO_B = getDormitoryStudentDOByStudentId(studentIdB);
        if (dormitoryStudentDO_A == null || dormitoryStudentDO_B == null) {
            log.error("未找到学生的宿舍分配记录，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
            throw new ClientException("未找到学生的宿舍分配记录");
        }

        Long dormitoryIdA = dormitoryStudentDO_A.getDormitoryId();
        Long dormitoryIdB = dormitoryStudentDO_B.getDormitoryId();

        DormitoryDO dormitoryDO_A = dormitoryService.getById(dormitoryIdA);
        DormitoryDO dormitoryDO_B = dormitoryService.getById(dormitoryIdB);
        if (dormitoryDO_A == null || dormitoryDO_B == null) {
            log.error("宿舍不存在，宿舍 A ID：{}，宿舍 B ID：{}", dormitoryIdA, dormitoryIdB);
            throw new ClientException("宿舍不存在");
        }

        // 4. 校验宿舍性别类型是否匹配
        if (dormitoryDO_A.getType() != userRespDTO_B.getGender() || dormitoryDO_B.getType() != userRespDTO_A.getGender()) {
            log.error("学生性别与目标宿舍类型不匹配，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
            throw new ClientException("学生性别与目标宿舍类型不匹配");
        }

        // 5. 更新学生的宿舍分配记录
        dormitoryStudentDO_A.setDormitoryId(dormitoryIdB); // 学生 A 分配到宿舍 B
        dormitoryStudentDO_B.setDormitoryId(dormitoryIdA); // 学生 B 分配到宿舍 A

        boolean updated_A = updateById(dormitoryStudentDO_A);
        boolean updated_B = updateById(dormitoryStudentDO_B);
        if (!updated_A || !updated_B) {
            log.error("更新宿舍分配记录失败，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
            throw new ClientException("更新宿舍分配记录失败");
        }

        // 6. 更新用户的 hasDormitory 状态（可选）
        userRespDTO_A.setHasDormitory(1); // 学生 A 仍然有宿舍
        userRespDTO_B.setHasDormitory(1); // 学生 B 仍然有宿舍

        UserUpdateReqDTO userUpdateReqDTO_A = BeanUtil.toBean(userRespDTO_A, UserUpdateReqDTO.class);
        UserUpdateReqDTO userUpdateReqDTO_B = BeanUtil.toBean(userRespDTO_B, UserUpdateReqDTO.class);

        int userUpdated_A = userService.update(userUpdateReqDTO_A);
        int userUpdated_B = userService.update(userUpdateReqDTO_B);
        if (userUpdated_A < 1 || userUpdated_B < 1) {
            log.error("更新用户宿舍状态失败，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
            throw new ClientException("更新用户宿舍状态失败");
        }

        log.info("交换学生宿舍成功，学生 A ID：{}，学生 B ID：{}", studentIdA, studentIdB);
    }

    // 辅助方法：根据学生 ID 查询宿舍分配记录（返回 DO 对象）
    private DormitoryStudentDO getDormitoryStudentDOByStudentId(Long studentId) {
        LambdaQueryWrapper<DormitoryStudentDO> queryWrapper = Wrappers.lambdaQuery(DormitoryStudentDO.class)
                .eq(DormitoryStudentDO::getStudentId, studentId);
        return baseMapper.selectOne(queryWrapper);
    }


    //    分割线
    @Override
    @Transactional(rollbackFor = Exception.class)
    public DormitoryDO assignDormitoryByCollaborativeFiltering(DormitoryStudentCFReqDTO requestParam) {
        Long userId = requestParam.getUserId();
        Integer gender = requestParam.getGender();

        // Step 1: 获取当前用户的偏好数据
        Map<String, Integer> userPreferences = getUserPreferences(userId);

        // Step 2: 获取所有符合条件的宿舍列表 (按性别分开)
        List<DormitoryDO> availableDormitories = getAvailableDormitoriesByGender(gender);

        if (availableDormitories.isEmpty()) {
            throw new IllegalArgumentException("没有找到符合条件的可用宿舍！");
        }

        // Step 3: 遍历宿舍，计算每个宿舍的综合相似度
        DormitoryDO bestDormitory = null;
        double highestSimilarityScore = -1.0;

        for (DormitoryDO dormitory : availableDormitories) {
            double dormitorySimilarityScore = calculateDormitorySimilarityScore(dormitory, userPreferences);
            if (dormitorySimilarityScore > highestSimilarityScore) {
                highestSimilarityScore = dormitorySimilarityScore;
                bestDormitory = dormitory;
            }
        }

        // 如果找不到合适的宿舍，则抛出异常
        if (bestDormitory == null) {
            throw new IllegalArgumentException("无法找到合适的宿舍！");
        }

        // Step 4: 分配宿舍
        allocateDormitoryToUser(bestDormitory.getDormitoryId(), userId);
        return bestDormitory;
    }

    /**
     * 获取用户偏好数据
     */
    private Map<String, Integer> getUserPreferences(Long userId) {
        Map<String, Object> preferenceMap = preferenceMapper.getUserPreferencesByUserId(userId);

        if (preferenceMap == null || preferenceMap.isEmpty()) {
            throw new IllegalArgumentException("用户偏好数据为空！");
        }

        return preferenceMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> convertToInteger(entry.getValue())
                ));
    }

    /**
     * 将 Object 类型的值转换为 Integer
     */
    private Integer convertToInteger(Object value) {
        if (value instanceof Number) {
            // 如果是数值类型（如 Integer、Double 等），直接转换为 Integer
            return ((Number) value).intValue();
        } else if (value instanceof Boolean) {
            // 如果是 Boolean 类型，true 转换为 1，false 转换为 0
            return (Boolean) value ? 1 : 0;
        } else {
            // 其他类型抛出异常或返回默认值
            throw new IllegalArgumentException("不支持的偏好数据类型：" + value.getClass());
        }
    }

    /**
     * 获取符合条件的可用宿舍列表（按性别筛选）
     */
    private List<DormitoryDO> getAvailableDormitoriesByGender(Integer gender) {
        // 创建 LambdaQueryWrapper 对象
        LambdaQueryWrapper<DormitoryDO> queryWrapper = new LambdaQueryWrapper<>();

        // 构建查询条件
        queryWrapper.eq(DormitoryDO::getType, gender) // 条件：type = gender
                .apply("current_occupancy < capacity"); // 条件：current_occupancy < capacity
        // 执行查询
        return dormitoryMapper.selectList(queryWrapper);
    }



    /**
     * 将用户分配到指定宿舍
     */
    private void allocateDormitoryToUser(Long dormitoryId, Long userId) {
        // 查询宿舍信息
        DormitoryDO dormitory = dormitoryMapper.selectById(dormitoryId);
        if (dormitory == null || dormitory.getCurrentOccupancy() >= dormitory.getCapacity()) {
            throw new IllegalArgumentException("宿舍已满或不存在！");
        }

        // 更新宿舍的当前人数
        dormitoryMapper.allocateUserToDormitory(dormitoryId);

        // 插入宿舍学生记录
        insertDormitoryStudentRecord(dormitoryId, userId);

        // 更新用户为已分配宿舍
        updateHasDormitoryFlag(userId);
    }


    /**
     * 更新用户为已分配宿舍
     */
    private void updateHasDormitoryFlag(Long userId) {
        UserDO user = new UserDO();
        user.setUserId(userId);
        user.setHasDormitory(1); // 更新为已分配宿舍
        userMapper.updateById(user);
    }

    /**
     * 计算加权余弦相似度
     */
    private double calculateWeightedCosineSimilarity(Map<String, Integer> prefs1, Map<String, Integer> prefs2) {
        double dotProduct = 0.0, normA = 0.0, normB = 0.0;
        for (String key : prefs1.keySet()) {
            int a = prefs1.getOrDefault(key, 0);
            int b = prefs2.getOrDefault(key, 0);
            double weight = PREFERENCE_WEIGHTS.getOrDefault(key, 1.0); // 默认权重为 1.0
            dotProduct += weight * a * b;
            normA += weight * Math.pow(a, 2);
            normB += weight * Math.pow(b, 2);
        }
        return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
    }

    /**
     * 计算宿舍的综合相似度分数（优化版）
     */
    private double calculateDormitorySimilarityScore(DormitoryDO dormitory, Map<String, Integer> userPreferences) {
        List<Long> roommateIds = dormitoryMapper.findRoommateIdsByDormitoryId(dormitory.getDormitoryId());
        double totalSimilarity = 0.0;

        for (Long roommateId : roommateIds) {
            Map<String, Integer> roommatePreferences = getUserPreferences(roommateId);
            totalSimilarity += calculateWeightedCosineSimilarity(userPreferences, roommatePreferences);
        }

        double averageSimilarity = roommateIds.isEmpty() ? 0.0 : totalSimilarity / roommateIds.size();

        // 动态调整空宿舍权重
        int capacity = dormitory.getCapacity();
        int currentOccupancy = dormitory.getCurrentOccupancy();
        double occupancyWeight = (capacity - currentOccupancy) / (double) capacity; // 空床位比例

        // 综合相似度分数 = 平均相似度 + 空床位权重（动态放大）
        return averageSimilarity + occupancyWeight * 3; // 放大空床位权重
    }


    /**
     * 计算余弦相似度
     */
//    private double calculateCosineSimilarity(Map<String, Integer> prefs1, Map<String, Integer> prefs2) {
//        double dotProduct = 0.0, normA = 0.0, normB = 0.0;
//        for (String key : prefs1.keySet()) {
//            int a = prefs1.getOrDefault(key, 0);
//            int b = prefs2.getOrDefault(key, 0);
//            dotProduct += a * b;
//            normA += Math.pow(a, 2);
//            normB += Math.pow(b, 2);
//        }
//        return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
//    }


    /**
     * 计算宿舍的综合相似度分数
     */
//    private double calculateDormitorySimilarityScore(DormitoryDO dormitory, Map<String, Integer> userPreferences) {
//        List<Long> roommateIds = dormitoryMapper.findRoommateIdsByDormitoryId(dormitory.getDormitoryId());
//        double totalSimilarity = 0.0;
//
//        for (Long roommateId : roommateIds) {
//            Map<String, Integer> roommatePreferences = getUserPreferences(roommateId);
//            totalSimilarity += calculateCosineSimilarity(userPreferences, roommatePreferences);
//        }
//
//        // 平均相似度分数
//        double averageSimilarity = roommateIds.isEmpty() ? 0.0 : totalSimilarity / roommateIds.size();
//
//        // 如果是空宿舍，增加额外的权重（例如 +1.0）
//        if (roommateIds.isEmpty()) {
//            averageSimilarity += 1.0; // 空宿舍的奖励权重
//        }
//
//        return averageSimilarity;
//    }

    //分割符
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignDormitoriesByCollaborativeFiltering(List<DormitoryStudentCFReqDTO> requestParams) {
        if (requestParams == null || requestParams.isEmpty()) {
            throw new IllegalArgumentException("请求参数为空！");
        }

        // Step 1: 提取所有用户ID和性别信息
        Map<Long, Integer> userIdToGenderMap = requestParams.stream()
                .collect(Collectors.toMap(
                        DormitoryStudentCFReqDTO::getUserId,
                        DormitoryStudentCFReqDTO::getGender
                ));

        // Step 2: 获取所有符合条件的宿舍列表
        List<DormitoryDO> availableDormitories = getAvailableDormitoriesByGenders(new ArrayList<>(userIdToGenderMap.values()));

        if (availableDormitories.isEmpty()) {
            throw new ClientException("没有找到符合条件的可用宿舍！");
        }

        // Step 3: 构建用户-宿舍相似度矩阵
        Map<Long, Map<Long, Double>> userDormSimilarityMatrix = buildUserDormSimilarityMatrix(requestParams, availableDormitories);

        // Step 4: 执行全局分配
        Map<Long, Long> allocationResult = performGlobalAssignment(userDormSimilarityMatrix, availableDormitories);

        // Step 5: 批量更新数据库
        for (Map.Entry<Long, Long> entry : allocationResult.entrySet()) {
            Long userId = entry.getKey();
            Long dormitoryId = entry.getValue();

            // 分配宿舍
            allocateDormitoryToUser(dormitoryId, userId);
        }
    }

    /**
     *
     */
    public Page<DormitoryStudentPageRespDTO> getAllDormitoryStudentByPage(int pageNum, int pageSize) {
        QueryWrapper<DormitoryStudentDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("dormitory_id");
        List<DormitoryStudentDO> dormitoryStudentDOs = dormitoryStudentMapper.selectList(queryWrapper);

        Map<Long, List<Long>> dormitoryToStudentsMap = dormitoryStudentDOs.stream()
                .collect(Collectors.groupingBy(DormitoryStudentDO::getDormitoryId,
                        Collectors.mapping(DormitoryStudentDO::getStudentId, Collectors.toList())));

        List<DormitoryStudentPageRespDTO> dtoList = dormitoryToStudentsMap.entrySet().stream()
                .map(entry -> {
                    DormitoryStudentPageRespDTO dto = new DormitoryStudentPageRespDTO();
                    dto.setDormitoryId(entry.getKey());
                    dto.setStudentIds(entry.getValue());
                    return dto;
                }).collect(Collectors.toList());

        int fromIndex = Math.min(pageSize * (pageNum - 1), dtoList.size());
        int toIndex = Math.min(fromIndex + pageSize, dtoList.size());
        List<DormitoryStudentPageRespDTO> pageContent = dtoList.subList(fromIndex, toIndex);

        Page<DormitoryStudentPageRespDTO> resultPage = new Page<>(pageNum, pageSize);
        resultPage.setRecords(pageContent);
        resultPage.setTotal(dtoList.size());

        return resultPage;
    }

    @Override
    public Page<DormitoryStudentDetailPageRespDTO> getAllDormitoryStudentDetailByPage(int pageNum, int pageSize) {
        // Step 1: 调用已有方法获取分组数据
        Page<DormitoryStudentPageRespDTO> basePage = this.getAllDormitoryStudentByPage(pageNum, pageSize);
        List<DormitoryStudentPageRespDTO> baseList = basePage.getRecords();

        // Step 2: 构建最终返回的详细 DTO 列表
        List<DormitoryStudentDetailPageRespDTO> detailList = baseList.stream()
                .map(baseDTO -> {
                    Long dormitoryId = baseDTO.getDormitoryId();
                    List<Long> studentIds = baseDTO.getStudentIds();

                    // 查询宿舍详情（可替换为真实服务）
                    DormitoryRespDTO dormitoryRespDTO = dormitoryService.getDormitoryById(String.valueOf(dormitoryId));

                    // 查询学生详情（可替换为真实服务）

                    List<UserDO> users = userService.getUsersByIds(studentIds);

                    // 封装详细 DTO
                    DormitoryStudentDetailPageRespDTO detailDTO = new DormitoryStudentDetailPageRespDTO();
                    detailDTO.setDormitoryId(dormitoryId);
                    if (dormitoryRespDTO != null) {
                        detailDTO.setDormitoryNumber(dormitoryRespDTO.getDormitoryNumber());
                        detailDTO.setType(dormitoryRespDTO.getType());
                        detailDTO.setIsAvailable(dormitoryRespDTO.getIsAvailable());
                    }
                    detailDTO.setUsers(users);

                    return detailDTO;
                })
                .collect(Collectors.toList());

        // Step 3: 构造分页对象并返回
        Page<DormitoryStudentDetailPageRespDTO> resultPage = new Page<>(pageNum, pageSize, (int) basePage.getTotal());
        resultPage.setRecords(detailList);
        return resultPage;
    }



    /**
     * 获取符合条件的可用宿舍列表（支持多种性别）
     */
    private List<DormitoryDO> getAvailableDormitoriesByGenders(List<Integer> genders) {
        LambdaQueryWrapper<DormitoryDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(DormitoryDO::getType, genders) // 条件：type in (genders)
                .apply("current_occupancy < capacity"); // 条件：current_occupancy < capacity
        return dormitoryMapper.selectList(queryWrapper);
    }

    /**
     * 构建用户-宿舍相似度矩阵（移除重复加权）
     */
    private Map<Long, Map<Long, Double>> buildUserDormSimilarityMatrix(
            List<DormitoryStudentCFReqDTO> requestParams,
            List<DormitoryDO> availableDormitories) {

        Map<Long, Map<Long, Double>> similarityMatrix = new HashMap<>();

        for (DormitoryStudentCFReqDTO param : requestParams) {
            Long userId = param.getUserId();
            Map<String, Integer> userPreferences = getUserPreferences(userId);

            Map<Long, Double> dormSimilarityScores = new HashMap<>();
            for (DormitoryDO dormitory : availableDormitories) {
                double similarityScore = calculateDormitorySimilarityScore(dormitory, userPreferences);
                dormSimilarityScores.put(dormitory.getDormitoryId(), similarityScore);
            }

            similarityMatrix.put(userId, dormSimilarityScores);
        }

        return similarityMatrix;
    }

    /**
     * 执行全局分配（贪心算法示例）
     */
    /**
     * 执行全局分配（改进版：综合考虑相似度和占用率）
     */
    private Map<Long, Long> performGlobalAssignment(
            Map<Long, Map<Long, Double>> similarityMatrix,
            List<DormitoryDO> availableDormitories) {

        Map<Long, Long> allocationResult = new HashMap<>();
        Map<Long, Integer> dormitoryOccupancy = availableDormitories.stream()
                .collect(Collectors.toMap(
                        DormitoryDO::getDormitoryId,
                        dormitory -> dormitory.getCurrentOccupancy()
                ));

        // 按用户-宿舍相似度从高到低排序
        List<Map.Entry<Long, Map<Long, Double>>> sortedEntries = similarityMatrix.entrySet().stream()
                .sorted((e1, e2) -> {
                    double maxScore1 = Collections.max(e1.getValue().values());
                    double maxScore2 = Collections.max(e2.getValue().values());
                    return Double.compare(maxScore2, maxScore1); // 降序排序
                })
                .collect(Collectors.toList());

        for (Map.Entry<Long, Map<Long, Double>> entry : sortedEntries) {
            Long userId = entry.getKey();
            Map<Long, Double> dormScores = entry.getValue();

            // 综合考虑相似度和宿舍占用率
            Optional<Map.Entry<Long, Double>> bestMatch = dormScores.entrySet().stream()
                    .filter(e -> dormitoryOccupancy.get(e.getKey()) < getDormitoryCapacity(e.getKey()))
                    .min(Comparator.comparingDouble(e -> { // 综合评分
                        double similarityScore = e.getValue();
                        int currentOccupancy = dormitoryOccupancy.get(e.getKey());
                        int capacity = getDormitoryCapacity(e.getKey());
                        double occupancyRatio = (double) currentOccupancy / capacity;
                        return -similarityScore + occupancyRatio; // 相似度越高、占用率越低越好
                    }));

            if (bestMatch.isPresent()) {
                Long dormitoryId = bestMatch.get().getKey();
                allocationResult.put(userId, dormitoryId);
                dormitoryOccupancy.put(dormitoryId, dormitoryOccupancy.get(dormitoryId) + 1);
            } else {
                throw new IllegalArgumentException("无法为用户 " + userId + " 分配宿舍！");
            }
        }

        return allocationResult;
    }

    /**
     * 获取宿舍容量
     */
    private int getDormitoryCapacity(Long dormitoryId) {
        DormitoryDO dormitory = dormitoryMapper.selectById(dormitoryId);
        if (dormitory == null) {
            throw new IllegalArgumentException("宿舍不存在！");
        }
        return dormitory.getCapacity();
    }


}
